package com.onpositive.knowledge.model.values;

import java.util.Map;

import com.onpositive.semantic.model.api.property.IProperty;
import com.onpositive.semantic.model.api.property.ValueUtils;

public abstract class ValueTraverser {
	
	static public final int PUT_OBJECT_IN_META = 2 ;
	static public final String META_KEY_CURRENT_OBJECT = "current-object";

	protected final Class<?>[] targetClasses ;
	protected final Class<?>[] necessaryClasses ;

	
	public ValueTraverser( Class<?>[] tc , Class<?>[] nc ) {
		super();
		this.targetClasses = tc ;
		this.necessaryClasses = nc ;		
	}
	
	public ValueTraverser() {
		super();
		this.targetClasses = createTargetClasses();
		this.necessaryClasses = createNecessaryClasses();
	}

	public void iterateProperties(Iterable<IProperty> properties, Iterable<Object> objects, Map<String,Object> metaInfo, int ind)
	{
		for( IProperty prop : properties )
			iterateObjects( prop ,objects, metaInfo, ind ) ;
	}

	public void iterateObjects( IProperty prop, Iterable<Object> objects, Map<String,Object> metaInfo, int ind)
	{
		for( Object obj : objects )
			itereteObjectValues(prop, obj,metaInfo, ind);
	}

	public void itereteObjectValues(IProperty prop, Object obj, Map<String, Object> metaInfo, int ind)
	{
		if( (ind & PUT_OBJECT_IN_META) != 0 ){
			metaInfo.put(META_KEY_CURRENT_OBJECT, obj) ;
		}
		Object value = prop.getValue( obj );
		if ( value == null )
			return ;
			
		Iterable<Object> collection = ValueUtils.toCollection(value);			
		for (Object val : collection){					
			iterateValues( prop, val, false, metaInfo, ind ) ;
			postIterateValue( prop, val, metaInfo, ind ) ;
		}
		
		
	}	

	protected void postIterateValue(IProperty prop, Object val,	Map<String, Object> metaInfo, int ind)
	{		
	}
	

	public void iterateValues(IProperty prop, Object val, boolean _hasNecessaryClasses, Map<String,Object> metaInfo, int index)
	{
		if( isTargetClass(val) || ( _hasNecessaryClasses && (!(val instanceof IHasValue) ) ) ){
			process( prop, val , metaInfo, index ) ;
			return ;
		}
		if(!( val instanceof IHasValue  )){
			
			if( val instanceof Iterable<?> )
				for( Object o : (Iterable<?>)val )
					iterateValues( prop, o, _hasNecessaryClasses, metaInfo, index) ;
			else if( val instanceof Object[] )
				for( Object o : (Object[])val )
					iterateValues( prop, o, _hasNecessaryClasses, metaInfo, index) ;
			return ;
		}
		
		boolean hasNecessaryClasses = false ;
		
		while ( val instanceof IHasValue  )
		{
			hasNecessaryClasses = hasNecessaryClasses || isNecessaryClass(val) ;
					
			val = ((IHasValue)val).value() ;
					
			if( isTargetClass(val) ){
				process( prop, val , metaInfo, index ) ;
				return ;
			}
		}
		
		if( val instanceof Iterable<?> ){
			for( Object o : (Iterable<?>)val )
				iterateValues( prop, o, hasNecessaryClasses, metaInfo, index) ;
		}
		else if( val instanceof Object[] ){
			for( Object o : (Object[])val )
				iterateValues( prop, o, hasNecessaryClasses, metaInfo, index) ;		
		}
		else
			iterateValues( prop, val, hasNecessaryClasses, metaInfo, index) ;
	}

	private boolean isNecessaryClass(Object val) {
		boolean hasNecessaryClass = necessaryClasses.length == 0 ;
		for( Class<?> clazz : necessaryClasses )
			hasNecessaryClass = hasNecessaryClass || clazz.isInstance(val) ;
				
		return hasNecessaryClass ;
	}

	private boolean isTargetClass(Object val) {
		boolean isTargetClass = false ;
		for( Class<?> clazz : targetClasses )
			if( clazz.isInstance(val) )
			{
				isTargetClass = true ;
				break ;
			}
		return isTargetClass;
	}

	protected abstract void process( IProperty prop, Object val, Map<String,Object> metaInfo, int index);

	protected abstract Class<?>[] createNecessaryClasses();

	protected abstract Class<?>[] createTargetClasses();

	public Class<?>[] getTargetClasses() {
		return targetClasses;
	}

	public Class<?>[] getNecessaryClasses() {
		return necessaryClasses;
	}

}